# coding: utf-8

# pip install HPSocket==0.9.3

import time, sys, os

from HPSocket import TcpPull
from HPSocket import helper
import HPSocket.pyhpsocket as HPSocket
import random
import json

class Pack:
    PROTOCOL_OFFSET_ADDRESS_LEN = 0  # 长度地址偏移
    PROTOCOL_OFFSET_ADDRESS_API = 4  # API地址偏移
    PROTOCOL_OFFSET_ADDRESS_SEND_TYPE = 6  # API地址偏移
    PROTOCOL_OFFSET_ADDRESS_FLAG = 8  # 标识地址偏移
    PROTOCOL_OFFSET_ADDRESS_DATA = 16  # 数据地址偏移
    PROTOCOL_HEADER_SIZE = 16
    PACK_FLAG_TYPE_ASYN = 10  # 异步类型
    PACK_FLAG_TYPE_SYNC = 11  # 同步类型
    PACK_FLAG_TYPE_RESPONSE = 12  # 响应类型

    def __init__(self):
        pass

    def Api_GetTotal_Size(self, pDate: bytes, off=0):
        total = pDate[off + self.PROTOCOL_OFFSET_ADDRESS_LEN:off + self.PROTOCOL_OFFSET_ADDRESS_LEN + 4]
        total = int.from_bytes(total, byteorder='little')
        return total

    def Api_GetApiCode(self, pDate: bytes, off=0):
        api_code = pDate[off + self.PROTOCOL_OFFSET_ADDRESS_API:off + self.PROTOCOL_OFFSET_ADDRESS_API + 2]
        api_code = int.from_bytes(api_code, byteorder='little')
        return api_code

    def Api_GetSendType(self, pDate: bytes, off=0):
        send_type = pDate[off + self.PROTOCOL_OFFSET_ADDRESS_SEND_TYPE:off + self.PROTOCOL_OFFSET_ADDRESS_SEND_TYPE + 2]
        send_type = int.from_bytes(send_type, byteorder='little')
        return send_type

    def Api_GetPackFlag(self, pDate: bytes, off=0):
        flag = pDate[off + self.PROTOCOL_OFFSET_ADDRESS_FLAG:off + self.PROTOCOL_OFFSET_ADDRESS_FLAG + 8]
        flag = int.from_bytes(flag, byteorder='little')
        return flag

    def Api_GetPackData(self, pDate: bytes, off=0):
        data = pDate[off + self.PROTOCOL_OFFSET_ADDRESS_DATA:]
        return data

    def Api_CreatePackFlag(self):
        s = ''
        for i in range(16):
            if i == 0:
                s = str(random.randint(1, 9))
            else:
                s = s + str(random.randint(0, 9))
        resdata = int(s)
        return resdata

    def Api_CreatePack(self, apiCode: int, sendType, packFlag: int, data: bytes):
        length = len(data) + 4
        d = length.to_bytes(4, byteorder="little")
        d = d + apiCode.to_bytes(2, byteorder="little")
        d = d + sendType.to_bytes(2, byteorder="little")
        d = d + packFlag.to_bytes(8, byteorder="little")
        return d + data

    def Api_DataUnPack(self, pData: bytes):
        pCurrent = 0
        Resdata = bytes()
        ApiCode = 0
        flag = 0
        send_type = 0
        received_size = len(pData)
        while True:
            if (received_size - pCurrent < self.PROTOCOL_HEADER_SIZE):
                break
            Total_Size = self.Api_GetTotal_Size(pDate=pData, off=pCurrent)
            if Total_Size + self.PROTOCOL_HEADER_SIZE - 4 > received_size - pCurrent:
                break
            ApiCode = self.Api_GetApiCode(pDate=pData, off=pCurrent)
            send_type = self.Api_GetSendType(pDate=pData, off=pCurrent)
            flag = self.Api_GetPackFlag(pDate=pData, off=pCurrent)
            ResData_Length = Total_Size - 4
            if ResData_Length > 0:
                Resdata = self.Api_GetPackData(pDate=pData, off=pCurrent)
            pCurrent = pCurrent + Total_Size + self.PROTOCOL_HEADER_SIZE - 4
        return (received_size - pCurrent, ApiCode, send_type, flag, Resdata)

class MySocketEvent:
    def __init__(self):
        pass
    def OnReceive(self, data, flag, ApiCode, ConnID):
        '''
        数据到达
        :param data: 数据
        :param flag: 标识
        :param ApiCode: APICODE
        :param ConnID: 连接ID 若是客户端 此参数为None
        :return:
        '''
        print("数据到达", ConnID, flag, ApiCode, data)
        return HPSocket.EnHandleResult.HR_OK
    def OnRequest(self, data, flag, ApiCode, ConnID):
        '''
        请求到达
        :param data: 数据
        :param flag: 标识
        :param ApiCode: APICODE
        :param ConnID: 连接ID 若是客户端 此参数为None
        :return:
        '''
        print("请求到达", ConnID, flag, ApiCode, data)
        return HPSocket.EnHandleResult.HR_OK
        # '''这里处理同步逻辑'''
        # msg = {"code": 200, "msg": "success"}
        # msg = json.dumps(msg)
        # if not ConnID:
        #     Client.Answer(flag=flag, data=msg.encode())
        # else:
        #     Server.Answer(ConnID=ConnID, flag=flag, data=msg.encode())
        # pass
        #

    def OnClose(self, Sender, ConnID, Operation, ErrorCode):
        '''
        连接断开
        :param ConnID:
        :param ErrorCode:
        :return:
        '''
        print("连接断开", Sender, ConnID, Operation, ErrorCode)
        return HPSocket.EnHandleResult.HR_OK

    def OnConnect(self,Sender, ConnID):
        '''
        客户进入
        :param Sender:
        :param ConnID:
        :return:
        '''
        print("连接成功",Sender, ConnID)
        return HPSocket.EnHandleResult.HR_OK

    def OnShutdown(self,Sender):
        '''
        服务器关闭
        :param Sender:
        :return:
        '''
        print("服务器关闭", Sender)
        return HPSocket.EnHandleResult.HR_OK

class Receive:
    def __init__(self, mySocketEvent: MySocketEvent):
        self.Pack = Pack()
        self.data = bytes()
        self.Response = []
        self.mySocketEvent = mySocketEvent

    def GetResponse(self, flag: int, outTime=5):
        t = time.time()
        rData = None
        while True:
            for i in range(len(self.Response)):
                if self.Response[i]["flag"] == flag:
                    data = self.Response.pop(i)
                    rData = data["data"]
                    break
            if rData:
                break
            if time.time() - t > outTime:
                break
        return rData

    def _Recevie(self, pData: bytes, ConnID):
        self.data = self.data + pData
        if len(self.data) < self.Pack.PROTOCOL_HEADER_SIZE:
            return 0
        (length, ApiCode, send_type, flag, resdata) = self.Pack.Api_DataUnPack(pData=self.data)
        if length == 0:
            if not flag == 0:
                if send_type == self.Pack.PACK_FLAG_TYPE_RESPONSE:
                    # print("收到响应包", flag)
                    self.Response.append({
                        "flag": flag,
                        "data": resdata
                    })
                elif send_type == self.Pack.PACK_FLAG_TYPE_SYNC:
                    self.mySocketEvent.OnRequest(resdata.decode(), flag, ApiCode, ConnID)
                else:
                    self.mySocketEvent.OnReceive(resdata.decode(), flag, ApiCode, ConnID)
            self.data = bytes()
        else:
            self.data = self.data[len(self.data) - length:]
        return 0


class Client(TcpPull.HP_TcpPullClient):
    def __init__(self, ip, port, mySocketEvent:MySocketEvent = None):
        super().__init__()
        HPSocket.HP_TcpClient_SetSocketBufferSize(self.Client, 0x3FFFFF)
        self.isConnect=False
        self.ip = ip
        self.port = port
        if mySocketEvent:
            self.mySocketEvent = mySocketEvent
        else:
            self.mySocketEvent = MySocketEvent()

        self._Recevie = Receive(mySocketEvent=self.mySocketEvent)
        self.Pack = Pack()

    @TcpPull.HP_TcpPullServer.EventDescription
    def Start(self):
        if super().Start(self.ip, self.port):
            print('Start Client success, listen on %s:%d' % (self.ip, self.port))
            HPSocket.HP_TcpClient_SetSocketBufferSize(self.Client, 0x3FFFFF)
        else:
            print('Start Client fail.')

    @TcpPull.HP_TcpPullClient.EventDescription
    def OnClose(self, Sender, ConnID, Operation, ErrorCode):
        self.isConnect = False
        return self.mySocketEvent.OnClose(Sender, ConnID, Operation, ErrorCode)

    @TcpPull.HP_TcpPullClient.EventDescription
    def OnConnect(self, Sender, ConnID):
        self.isConnect = True
        return self.mySocketEvent.OnConnect(Sender, ConnID)

    @TcpPull.HP_TcpPullClient.EventDescription
    def OnReceive(self, Sender, ConnID, Length):
        result = HPSocket.HP_TcpPullClient_Fetch(Sender, Length)  # 这里获取 Body
        return self._Recevie._Recevie(result, ConnID)

    def Sync_SendData(self, apiCode, data: bytes, outTime: int):
        packFlag = self.Pack.Api_CreatePackFlag()
        pData = self.Pack.Api_CreatePack(apiCode=apiCode,
                                         sendType=self.Pack.PACK_FLAG_TYPE_SYNC,
                                         packFlag=packFlag,
                                         data=data)
        self.Send(self.Client, pData)
        Response = self._Recevie.GetResponse(flag=packFlag, outTime=outTime)
        return Response

    def Answer(self, flag: int, apiCode, data: bytes):
        pData = self.Pack.Api_CreatePack(apiCode=apiCode,
                                         sendType=self.Pack.PACK_FLAG_TYPE_RESPONSE,
                                         packFlag=flag,
                                         data=data)
        return self.Send(self.Client, pData)

    def SendData(self, apiCode, data: bytes):
        packFlag = self.Pack.Api_CreatePackFlag()
        pData = self.Pack.Api_CreatePack(apiCode=apiCode,
                                         sendType=self.Pack.PACK_FLAG_TYPE_ASYN,
                                         packFlag=packFlag,
                                         data=data)
        return self.Send(self.Client, pData)


class Server(TcpPull.HP_TcpPullServer):
    def __init__(self, ip, port, mySocketEvent: MySocketEvent = None):
        super().__init__()
        HPSocket.HP_TcpClient_SetSocketBufferSize(self.Server, 0x3FFFFF)
        self.ip = ip
        self.port = port
        if mySocketEvent:
            self.mySocketEvent = mySocketEvent
        else:
            self.mySocketEvent = MySocketEvent()
        self._Recevie = Receive(mySocketEvent=self.mySocketEvent)
        self.Pack = Pack()

    @TcpPull.HP_TcpPullServer.EventDescription
    def Start(self):
        if super().Start(self.ip, self.port):
            print('Start server success, listen on %s:%d' % (self.ip, self.port))
            return True
        else:
            print('Start server fail.')
            return False

    @TcpPull.HP_TcpPullServer.EventDescription
    def OnShutdown(self, Sender):
        self.mySocketEvent.OnShutdown(Sender)
        return HPSocket.EnHandleResult.HR_OK

    @TcpPull.HP_TcpPullServer.EventDescription
    def OnPrepareListen(self, Sender, SocketHandler):
        return HPSocket.EnHandleResult.HR_OK

    @TcpPull.HP_TcpPullServer.EventDescription
    def OnSend(self, Sender, ConnID, Data):
        return HPSocket.EnHandleResult.HR_OK

    @TcpPull.HP_TcpPullServer.EventDescription
    def OnClose(self, Sender, ConnID, Operation, ErrorCode):
        return self.mySocketEvent.OnClose(Sender, ConnID, Operation, ErrorCode)


    @TcpPull.HP_TcpPullServer.EventDescription
    def OnAccept(self, Sender, ConnID, Client):
        '''
        客户进入
        :param Sender:
        :param ConnID:
        :param Client:
        :return:
        '''
        pkg = helper.TPkgInfo(True, helper.TPkgHeaderSize)  # 这块内存需要保护起来
        HPSocket.HP_Server_SetConnectionExtra(Sender, ConnID, pkg)
        return self.mySocketEvent.OnConnect(Sender, ConnID)

    @TcpPull.HP_TcpPullServer.EventDescription
    def OnReceive(self, Sender, ConnID, Length):
        result = HPSocket.HP_TcpPullServer_Fetch(Sender, ConnID, Length)  # 这里获取 Body
        return self._Recevie._Recevie(result, ConnID=ConnID)

    def Sync_SendData(self, ConnID, apiCode, data: bytes):
        packFlag = self.Pack.Api_CreatePackFlag()
        pData = self.Pack.Api_CreatePack(apiCode=apiCode,
                                         sendType=self.Pack.PACK_FLAG_TYPE_SYNC,
                                         packFlag=packFlag,
                                         data=data)
        self.Send(self.Server, ConnID, pData)
        Response = self._Recevie.GetResponse(flag=packFlag, outTime=15)
        return Response

    def Answer(self, ConnID, apiCode, flag: int, data: bytes):
        pData = self.Pack.Api_CreatePack(apiCode=apiCode,
                                         sendType=self.Pack.PACK_FLAG_TYPE_RESPONSE,
                                         packFlag=flag,
                                         data=data)
        return self.Send(self.Server, ConnID, pData)

    def SendData(self, ConnID, apiCode, data: bytes):
        packFlag = self.Pack.Api_CreatePackFlag()
        pData = self.Pack.Api_CreatePack(apiCode=apiCode,
                                         sendType=self.Pack.PACK_FLAG_TYPE_ASYN,
                                         packFlag=packFlag,
                                         data=data)
        return self.Send(self.Server, ConnID, pData)


if __name__ == '__main__':

    svr = Server('127.0.0.1', 7889)
    svr.Start()
    # msg = "序号【%d】：我是服务器，测试数据！" % (1)
    # msg = msg.encode()
    # svr.SendData(ConnID=1, apiCode=999, data=msg)
    #
    # msg = "序号【%d】：我是服务器，请求数据！" % (2)
    # msg = msg.encode()
    # Response = svr.Sync_SendData(ConnID=1, apiCode=999, data=msg)
    # print("清求回复：", Response)

    cnt = Client('127.0.0.1', 7889)
    cnt.Start()
    # msg = "序号【%d】：我是客户端，测试数据！" % (1)
    # msg = msg.encode()
    # cnt.SendData(apiCode=999, data=msg)
    import json
    msg = {"username":"新空间文化用品","password":"chaoer520@@#","cookies":[],"fingerprint":{},"outTime":30}
    msg = json.dumps(msg)
    msg = msg.encode()
    _TAOBAO_LOGIN=601
    response = cnt.Sync_SendData(apiCode=_TAOBAO_LOGIN, data=msg,outTime=30)
    if(response):
        print(response.decode())
    else:
        print(response)

    # cnt.SendData(apiCode=_TAOBAO_LOGIN, data=msg)
    # while True:
    #     time.sleep(1)

